#include "LightHandler.h"
#include "LightHandlerThread.h"
#include <iostream>
#include "Lightenums.h"
#include "Trigon.h"
#include <cstdlib>
#include <complex>

using namespace osg;
using namespace std;

#define NUMLIGHTTHREADS 11

LightHandlerThread * threadsL[NUMLIGHTTHREADS];
OpenThreads::Mutex lockmutexL;


/*
 * Konstruktor. Bekommt den Lightmanager und ein HUD-Objekt
 * übergeben. Mappt die benutzen Knöpfe auf
 * Methoden und initialisiert ein Thread-Array für die Licht-Threads.
 * Es gibt PUSH und SWITCH Knöpfe. Ein PUSH-Thread läuft nur so
 * lange die Taste gedrückt bleibt. Ein SWITCH-Thread wird per
 * Taste ein- und ausgeschaltet.
 */

LightHandler::LightHandler(LightManager* lightManager, HUD * hud):
        _lightManager(lightManager), _hud(hud), couchSpotOn(false) {
	keyToHandle('1', SWITCHLIGHTONE, PUSH);
	keyToHandle('2', SWITCHLIGHTTWO, PUSH);
	keyToHandle('3', SWITCHLIGHTTHREE, SWITCH);
	keyToHandle('4', SWITCHLIGHTFOUR, SWITCH);
	keyToHandle('5', SWITCHLIGHTFIVE, SWITCH);
	keyToHandle('6', SWITCHLIGHTSIX, SWITCH);
	keyToHandle('7', SWITCHLIGHTSEVEN, PUSH);
	keyToHandle('8', SWITCHLIGHTEIGHT, PUSH);
	keyToHandle('9', SWITCHLIGHTNINE, SWITCH);
	keyToHandle('0', SWITCHLIGHTTEN, SWITCH);
	keyToHandle('+', SWITCHLIGHTELEVEN, SWITCH);

	srand((unsigned)time(NULL));

	currentRR = 0.0f;
	currentGR = 0.0f;
	currentBR = 0.0f;
	currentRS = 0.0f;
	currentGS = 0.0f;
	currentBS = 0.0f;

	for (int i = 0; i < NUMLIGHTTHREADS; i++){
		threadsL[i] = new LightHandlerThread(this, i, 10000);
		threadsL[i]->quit();
	}

	Vec4d vec;
	for(int i = ONE; i <= FOUR; i++)
	{
		light = _lightManager->getOrCreateLight((LIGHTNUMBER)i);
		vec = light->getAmbient();
		#ifdef DEBUG
		cout << "Licht Nr. " << i << endl;
		cout << "AMBIENT: " << vec.x() << "," << vec.y() << "," << vec.z() << "," << vec.w() << endl;
		vec = light->getDiffuse();
		cout << "DIFFUSE: " << vec.x() << "," << vec.y() << "," << vec.z() << "," << vec.w() << endl;
		vec = light->getDiffuse();
		cout << "SPECULAR: " << vec.x() << "," << vec.y() << "," << vec.z() << "," << vec.w() << endl;
		#endif
	}
	createDefaultLightning();
}

LightHandler::~LightHandler(){
#ifdef DEBUG
	cout << "----- Destruiert: LightHandler -----" << endl;
#endif
}

/*
 * Wird aufgerufen, wenn eine Taste gedückt oder losgelassen wurde.
 * Wurde eine gemappte Taste gedrückt, wird der zugehörige
 * Licht-Thread gestartet. Wird eine Taste losgelassen,
 * wird der zugehörige Thread gestoppt.
 */

bool LightHandler::handle(const osgGA::GUIEventAdapter &ea,
	osgGA::GUIActionAdapter &aa) {
if (!_lightManager) return false;

	newKeyDownEvent = false;
	newKeyUpEvent   = false;

	 switch(ea.getEventType()){
	   case(osgGA::GUIEventAdapter::KEYDOWN):{

		// Key für Threadnamenanzeige wird gesondert abgegriffen
		if (ea.getKey() == 'h')
		   _hud->toggleShowNames();

		itr = keyFuncMap.find(ea.getKey());

		 if (itr != keyFuncMap.end()){

		    if ( (*itr).second.keyState == KEY_UP ){
				(*itr).second.keyState = KEY_DOWN;
				newKeyDownEvent = true;
		    }

		    if (newKeyDownEvent){
				m = (*itr).second.keyAction;
				if ((*itr).second.btnType == SWITCH){
					if ((*itr).second.swStatus == ON){

						(*itr).second.swStatus = OFF;
						threadsL[m]->quit();
						_hud->setThreadStatus(m, false);
						newKeyDownEvent = false;
						return true;
					}
					(*itr).second.swStatus = ON;
				}

				threadsL[m]->reset();
				threadsL[m]->start();
				_hud->setThreadStatus(m, true);
				newKeyDownEvent = false;
		    }
		    return true;
		 }
		 return false;
	 }

	   case(osgGA::GUIEventAdapter::KEYUP):{

		 itr = keyFuncMap.find(ea.getKey());

		 if (itr != keyFuncMap.end() )
			(*itr).second.keyState = KEY_UP;

		 itr = keyUPFuncMap.find(ea.getKey());
		 if (itr != keyUPFuncMap.end()) {

			m = (*itr).second.keyAction;
			if ((*itr).second.btnType == PUSH){
				threadsL[m]->quit();
				_hud->setThreadStatus(m, false);
			}
			return true;
		 }
		 return false;
	   }

	   default:
	      return false;
	   }
}

/*
 * Fügt Tasten in die keymaps ein.
 */


void LightHandler::keyToHandle(const int k, const LIGHTSWITCHES a,
		const BUTTONTYPE t){
	keyFuncMap[k].keyAction = a;
	keyFuncMap[k].btnType = t;
	keyUPFuncMap[k].keyAction = a;
	keyUPFuncMap[k].btnType = t;
	keyFuncMap[k].swStatus = OFF;
	keyUPFuncMap[k].swStatus = OFF;
}

/*
 * Zugriff auf den gemeinsamen Mutex für alle Threads
 */

OpenThreads::Mutex * LightHandler::getLockMutex(){
	return &lockmutexL;
}




/*
 * ------------------------------------------------------------------
 * 						LICHTER
 * ------------------------------------------------------------------
 *
 */



/*
 * Toggled ein Spotlight auf die Couch
 */


void LightHandler::toggleCouchSpot(const osg::Vec4d color){

	if (!couchSpotOn){
		couchSpotOn = true;
		createSpot(SEVEN, osg::Vec4d(-82.0f,-130.0f,38.0f, 1.0),
				color, osg::Vec3d(0,0,-1), 7.0f);
	}
	else {
		couchSpotOn = false;
		osg::Light * _light = _lightManager->getOrCreateLight(SEVEN);
		_light->setAmbient(osg::Vec4d(0.0, 0.0, 0.0, 0.0));
		_light->setDiffuse(osg::Vec4d(0.0, 0.0, 0.0, 0.0));
		_light->setSpecular(osg::Vec4d(0.0, 0.0, 0.0, 0.0));
	}
}

/*
 * Schaltet ein Spotlight auf die Diskokugel ein- oder aus.
 */

void LightHandler::discoOrbSpot(const bool on, const osg::Vec4d color){

	if (on){
		createSpot(THREE, osg::Vec4d(-2.0f,-80.0f,10.0f, 1.0), color,
				osg::Vec3d(-3.0f,4.0f,33.0f), 0.8);
	}
	else {
		osg::Light * _light = _lightManager->getOrCreateLight(THREE);
		_light->setAmbient(osg::Vec4d(0.0, 0.0, 0.0, 0.0));
		_light->setDiffuse(osg::Vec4d(0.0, 0.0, 0.0, 0.0));
		_light->setSpecular(osg::Vec4d(0.0, 0.0, 0.0, 0.0));
	}
}

/*
 * Schaltet ein Stroboskoblicht ein oder aus.
 */

void LightHandler::strobo(const bool on){
	osg::Vec4d color;
	if (on)
		color = osg::Vec4d(1.0f, 1.0f, 1.0f, 1.0f);	
	else
		color = osg::Vec4d(0.0f, 0.0f, 0.0f, 0.0f);

	osg::Light * _light = _lightManager->getOrCreateLight(SIX);
	_light->setPosition(osg::Vec4d(-2.0f,-80.0f,10.0f, 1.0));
	_light->setAmbient(color);
	_light->setDiffuse(color);
	_light->setSpecular(color);
}

/*
 * Schaltet ein zufällig wechselndes Licht. FOUR für
 * Spotlicht. FIVE für Raumlicht.
 */

void LightHandler::softSwitch(const int lightNumber){
	unsigned int steps = 10;
	unsigned int sleep;
	double step = 0.1f;
	double r = 0.0f, g = 0.0f, b = 0.0f;
	osg::Vec4d color;
	osg::Light * _light;

	switch(lightNumber){
		case FOUR:
			sleep = 27000;
			_light = _lightManager->getOrCreateLight(FOUR);
			_light->setPosition(osg::Vec4d(-2.0f,-80.0f,35.0f, 1.0));
			_light->setDirection(osg::Vec3d(-3.0f,4.0f,10.0f));
			_light->setSpotCutoff(90.0);
			_light->setSpotExponent(0.8);
			break;
		case FIVE:
			sleep = 45000;
			_light = _lightManager->getOrCreateLight(FIVE);
			_light->setPosition(osg::Vec4d(-2.0f,-80.0f,35.0f, 1.0));
			_light->setDirection(osg::Vec3d(-3.0f,4.0f,10.0f));
			break;
		default:
			return;
		}

	for (unsigned int i = 0; i < steps; i++){
		r+=step;
		color = osg::Vec4d(r, g, b, 1.0);
		_light->setAmbient(color);
		_light->setDiffuse(color);
		_light->setSpecular(color);
		OpenThreads::Thread::microSleep(sleep);
	}
	for (unsigned int i = 0; i < steps; i++){
		g+=step;
		color = osg::Vec4d(r, g, b, 1.0);
		_light->setAmbient(color);
		_light->setDiffuse(color);
		_light->setSpecular(color);
		OpenThreads::Thread::microSleep(sleep);
	}
	for (unsigned int i = 0; i < steps; i++){
		b+=step;
		color = osg::Vec4d(r, g, b, 1.0);
		_light->setAmbient(color);
		_light->setDiffuse(color);
		_light->setSpecular(color);
		OpenThreads::Thread::microSleep(sleep);
	}

	for (unsigned int i = 0; i < steps; i++){
		r-=step;
		color = osg::Vec4d(r, g, b, 1.0);
		_light->setAmbient(color);
		_light->setDiffuse(color);
		_light->setSpecular(color);
		OpenThreads::Thread::microSleep(sleep);
	}
	for (unsigned int i = 0; i < steps; i++){
		g-=step;
		color = osg::Vec4d(r, g, b, 1.0);
		_light->setAmbient(color);
		_light->setDiffuse(color);
		_light->setSpecular(color);
		OpenThreads::Thread::microSleep(sleep);
	}
	for (unsigned int i = 0; i < steps; i++){
		b-=step;
		color = osg::Vec4d(r, g, b, 1.0);
		_light->setAmbient(color);
		_light->setDiffuse(color);
		_light->setSpecular(color);
		OpenThreads::Thread::microSleep(sleep);
	}
}

/*
 * Schaltet ein zufällig wechselndes Licht. FOUR für
 * Spotlicht. FIVE für Raumlicht.
 */

void LightHandler::softSwitchRandom(const int lightNumber){
	double * curR, * curG, * curB;
	osg::Vec4d color;
	osg::Light * _light;

	// Schritte und Sleepzeit festlegen
	unsigned int sleep = 10000;
	unsigned int steps = 100;

	// Zufällige RGB werte zwischen 0.0 und 1.0 erzeugen
	double targetR=((double)rand()/(double)RAND_MAX);
	double targetG=((double)rand()/(double)RAND_MAX);
	double targetB=((double)rand()/(double)RAND_MAX);


	// Prüfen ob Licht FOUR (Spot) oder FIVE (Raumlicht)
	// benutzt werden soll. Dieses wird in _light geladen
	// und die Lichteigenschaften werden festgelegt.
	// für die Ausgangwerte des jeweiligen Lichts werden
	// die double Pointer curX auf das jeweilige Licht gesetzt.

	switch(lightNumber){
	case FOUR:
		_light = _lightManager->getOrCreateLight(FOUR);
		_light->setPosition(osg::Vec4d(-2.0f,-80.0f,35.0f, 1.0));
		_light->setDirection(osg::Vec3d(-3.0f,4.0f,10.0f));
		_light->setSpotCutoff(90.0);
		_light->setSpotExponent(0.4);
		curR = &currentRS;
		curG = &currentGS;
		curB = &currentBS;
		break;
	case FIVE:
		_light = _lightManager->getOrCreateLight(FIVE);
		_light->setPosition(osg::Vec4d(-2.0f,-80.0f,35.0f, 1.0));
		_light->setDirection(osg::Vec3d(-3.0f,4.0f,10.0f));
		curR = &currentRR;
		curG = &currentGR;
		curB = &currentBR;
		break;
	default:
		return;
	}

	// Schrittweite der jeweiligen Farbe errechnet sich
	// aus der absoluten Differenz zwischen der Start-
	// und Zielfarbe geteilt durch die Anzahl der Schritte.

	double stepWideR = abs(targetR - *curR)/steps;
	double stepWideG = abs(targetG - *curG)/steps;
	double stepWideB = abs(targetB - *curB)/steps;

	// Schleife setzt die Farben in Richtung Zielfarbe,
	// sofern diese noch nicht erreicht wurde.

	for (unsigned int i = 0; i < steps; i++){
		if (targetR != *curR){
			if (targetR > *curR)
				*curR += stepWideR;
			else
				*curR -= stepWideR;
		}
		if (targetG != *curG){
			if (targetG > *curG)
				*curG += stepWideG;
			else
				*curG -= stepWideG;
		}
		if (targetB != *curB){
			if (targetB > *curB)
				*curB += stepWideB;
			else
				*curB -= stepWideB;
		}

		color = osg::Vec4d(*curR, *curG, *curB, 1.0);
		_light->setAmbient(color);
		_light->setDiffuse(color);
		_light->setSpecular(color);
		OpenThreads::Thread::microSleep(sleep);
	}
}

/*
* Vorlage für das Erstellen eines Spots
*/
void LightHandler::createSpot(const LIGHTNUMBER number, const osg::Vec4d position,
		const osg::Vec4d color, const osg::Vec3d direction, const float spotExponent)
{
	light = _lightManager->getOrCreateLight(number);
	light->setPosition(position);
	light->setAmbient(color);
	light->setDiffuse(color);
	light->setSpecular(color);
	light->setDirection(direction);
	light->setSpotCutoff(90.0f);
	light->setSpotExponent(spotExponent);
}

void LightHandler::createDefaultLightning()
{
	createLights();
	for(int i = ONE; i <= FOUR; i++)
	{
		light = _lightManager->getOrCreateLight((LIGHTNUMBER)i);
		light->setAmbient(osg::Vec4d(0.0, 0.0, 0.0, 1.0));
		light->setSpecular(osg::Vec4d(0.8,0.8,0.8,1));
		light->setDiffuse(osg::Vec4d(0.8,0.8,0.8,1));
	}
}

/*
 * Erstellt einige statische Lichter
 */


void LightHandler::createLights(){

	light = _lightManager->getOrCreateLight(ONE);
	light->setAmbient(osg::Vec4d(0.3, 0.2, 0.6, 1.0)); 
	light->setPosition(osg::Vec4d(-5.23811f,10.4991f,4.0f, 1.0)); 

	light = _lightManager->getOrCreateLight(TWO);
	light->setAmbient(osg::Vec4d(0.9, 0.9, 0.9, 1.0)); 
	light->setPosition(osg::Vec4d(2.97485f,-0.015064f,4.0f, 1.0)); 

	light = _lightManager->getOrCreateLight(THREE);
	light->setAmbient(osg::Vec4d(0.1, 0.1, 0.1, 1.0)); 
	light->setPosition(osg::Vec4d(-12.0f,25.0f,12.0f, 1.0)); 

	light = _lightManager->getOrCreateLight(FOUR);
	light->setAmbient(osg::Vec4d(0.3, 0.3, 0.3, 1.0)); 
	light->setPosition(osg::Vec4d(1.99099f,-24.6132f,4.5f, 1.0)); 	

}


/*
 * Schaltet die statischen Licher aus
 */

void LightHandler::removeLights(){

	for(int i = ONE; i <= EIGHT; i++)
	{
		light = _lightManager->getOrCreateLight((LIGHTNUMBER)i);
		light->setAmbient(osg::Vec4d(0.0, 0.0, 0.0, 1.0)); 
		light->setSpecular(osg::Vec4d(0.0, 0.0, 0.0, 1.0)); 
		light->setDiffuse(osg::Vec4d(0.0, 0.0, 0.0, 1.0)); 
	}
}
